package com.woolink.per.sys.security.datasource;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;

import com.woolink.per.sys.authority.dao.AuthorityDao;

/**
 * 最核心的地方，就是提供某个资源对应的权限定义，即getAttributes方法返回的结果。 此类在初始化时，应该取到所有资源及其对应角色的定义。
 * @author wuliquan
 * @version 2014-1-2
 * @see MyInvocationSecurityMetadataSourceService
 * @since
 */
public class MyInvocationSecurityMetadataSourceService implements
        FilterInvocationSecurityMetadataSource
{
    private AuthorityDao authorityDao;
    
    private UrlMatcher urlMatcher = new AntUrlPathMatcher();
    
    private static Map<String, Collection<ConfigAttribute>> resourceMap = new HashMap<String, Collection<ConfigAttribute>>();;
    
    public MyInvocationSecurityMetadataSourceService(AuthorityDao authorityDao)
    {
        this.authorityDao = authorityDao;
        loadResourceDefine();
    }
    
    // 在Web服务器启动时，加载所有资源与权限的关系
    private void loadResourceDefine()
    {
        // 1.提取所有权限
        List<String> authorities = this.authorityDao.findAllAuthoritiesName();
        
        /*
         * 应当是资源为key， 权限为value。 资源通常为url， 权限就是那些以ROLE_为前缀的角色。 一个资源可以由多个权限来访问。
         * 
         * Map<资源，权限>
         */
        if(resourceMap == null)
        {
            resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
        }
        

        // 2.根据权限名获取对应的资源url
        for (String auth : authorities) {
            
            ConfigAttribute ca = new SecurityConfig(auth);

            // 3.获取权限所拥有的所有资源url
            List<String> resources = authorityDao.findResourcesUrl(auth);

            for (String res : resources) {
                
                String url = res;

                /*
                 * 判断资源文件和权限的对应关系，如果已经存在相关的资源url，则要通过该url为key提取出权限集合，将权限增加到权限集合中。
                 * 
                 */
                if (resourceMap.containsKey(url)) {
                    Collection<ConfigAttribute> value = resourceMap.get(url);
                    value.add(ca);
                    resourceMap.put(url, value);
                } else {
                    Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
                    atts.add(ca);
                    resourceMap.put(url, atts);
                }
            }

        }
        
        System.out.println("-----------");
        
    }
    
    public Collection<ConfigAttribute> getAllConfigAttributes()
    {
        
        return null;
    }
    
    // 根据URL，找到相关的权限
    public Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException
    {
        
        // object 是一个URL，被用户请求的url
        String url = ((FilterInvocation)object).getRequestUrl();
        
        // 判断url是否含有?
        int firstQuestionMarkIndex = url.indexOf("?");
        
        if (firstQuestionMarkIndex != -1)
        {
            url = url.substring(0, firstQuestionMarkIndex);
        }
        
        Iterator<String> ite = resourceMap.keySet().iterator();
        
        while (ite.hasNext())
        {
            String resURL = ite.next();
            
            if (urlMatcher.pathMatchesUrl(url, resURL))
            {
                return resourceMap.get(resURL);
            }
        }
        return null;
    }
    
    public boolean supports(Class<?> arg0)
    {
        return true;
    }

    /**
     * @param authorityDao The authorityDao to set.
     */
    public void setAuthorityDao(AuthorityDao authorityDao)
    {
        this.authorityDao = authorityDao;
    }

    /**
     * @return Returns the authorityDao.
     */
    public AuthorityDao getAuthorityDao()
    {
        return authorityDao;
    }
    
}
